package movEat.Kernel;

import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Map;
import java.util.Properties;
import java.util.Random;
import movEat.Definitions.DishTypes;
import movEat.Definitions.Pair;
import movEat.Definitions.actorDB;
import movEat.Generic.ConfigInfo;
import movEat.Generic.Error;
import movEat.IGui.*;
import movEat.IGui.OrderDetails.*;
import movEat.IKernel.*;
import movEat.Kernel.DataBase.DataBaseManaging;
import movEat.Kernel.Import.Importer;
import movEat.Kernel.OrderQueryImpl.*;
import movEat.Kernel.ThreadManaging.*;

public class Kernel implements IKernel{

	static private IGui gui;

	private ConfigInfo configInfo;

	@Override
	public void Start() {

		configInfo = new ConfigInfo();
		try {
			ParseConfigurationFile(configInfo);
		} catch (IOException e) {
			gui.SetError(new Error(false, "Configuration file was not found.\nUsing default configurations."));
		}

		ThreadManaging.init(10);

		DataBaseManaging.init(configInfo);

		gui.open();

	}

	private boolean ParseConfigurationFile(ConfigInfo configInfo) throws IOException {
		Properties properties = new Properties();

		InputStream s = new FileInputStream("configuration/config.properties");
		properties.load(s);

		String sNumOfThreads = properties.getProperty("NUMBER_OF_THREADS");
		
		configInfo.setPathOfDBFiles(properties.getProperty("DATASET_FILES_PATH"));

		String[] keyWords = properties.getProperty("DRINKS_KEYWORDS").split(",");
		configInfo.setKeyWords(keyWords);
		
		configInfo.setDbUserName(properties.getProperty("DB_USERNAME"));
		configInfo.setDbPassword(properties.getProperty("DB_PASSWORD"));
		configInfo.setExtension(properties.getProperty("FILES_EXTENSION",".list.gz"));
		configInfo.setPort(properties.getProperty("PORT","3305"));
		configInfo.setSchema(properties.getProperty("SCHEMA","DbMysql13"));

		return true;

	}

	@Override
	public void SetGui(IGui gui) {
		Kernel.gui = gui;
	}

	private void ImportButtonPushed() {
		configInfo.setPathOfDBFiles(gui.getDBFilesPath()+"\\");

		ThreadManaging.Execute(new Runnable() {

			@Override
			public void run() {
				try {

					Importer.Import(configInfo);
				} catch (Exception e) {
					gui.SetError(new Error(false, "Data Base Error: Failed to Import Data."));
					gui.SetMessage("Import Data Error: "+e.getMessage());
				}

			}
		});

	}

	private void OrderButtonPushed(final IOrderDetails orderDetails) {

		if (orderDetails.GetAppetizerDetails() != null){
			ThreadManaging.Execute(new Runnable() {

				@Override
				public void run() {

					try{
						/* Appetizer query */
						gui.SetMessage("Searching Appetizers...");

						IAppetizerOrderDetails appetizerOrderDetails = orderDetails.GetAppetizerDetails();
						ArrayList<Pair<String, Integer>> appetizerResult = DataBaseManaging.getAppetizers(appetizerOrderDetails);

						if (appetizerResult != null){
							gui.SetMessage("Search came up with "+appetizerResult.size()+" Appetizers.");

							IQueryResult appetizerQueryResult = new QueryResult(DishTypes.APPETIZER, appetizerResult);
							gui.SetQueryResult(appetizerQueryResult);
						}

						else
						{
							gui.SetError(new Error(false,"Data Base Error: Failed to query the DataBase for appetizers"));
						}

					}
					catch(SQLException e){
						gui.SetError(new Error(false,"Data Base Error: Failed to query the DataBase for appetizers"));
						gui.SetMessage("Query for appetizers Error: "+e.getMessage());
					}
				}
			});
		}

		if (orderDetails.GetMainCourseDetails() != null){
			ThreadManaging.Execute(new Runnable() {

				@Override
				public void run() {

					try {
						/* Main Course query */
						gui.SetMessage("Searching Main Course...");

						ArrayList<Pair<String, Integer>> mainCourseResult = null;
						IMainCourseOrderDetails mainCourseOrderDetails = orderDetails.GetMainCourseDetails();

						if(mainCourseOrderDetails.getActor()!=null){
							String imdb_name = DataBaseManaging.getActorImdbName(mainCourseOrderDetails.getActor());
							mainCourseOrderDetails.setActor(imdb_name);
						}

						mainCourseResult = DataBaseManaging.getMainCourse(mainCourseOrderDetails);

						if (mainCourseResult != null){

							gui.SetMessage("Search came up with "+mainCourseResult.size()+" Main Courses.");
							IQueryResult mainCourseQueryResult = new QueryResult(DishTypes.MAINCOURSE, mainCourseResult);
							gui.SetQueryResult(mainCourseQueryResult);
						}

						else
						{
							gui.SetError(new Error(false,"Data Base Error: Failed to query the DataBase for main course."));
						}
					}
					catch(SQLException e){
						gui.SetError(new Error(false,"Data Base Error: Failed to query the DataBase for main course."));
						gui.SetMessage("Query for main course Error: "+e.getMessage());		


					}

				}
			});

		}

		if (orderDetails.GetDrinksDetails() != null){
			ThreadManaging.Execute(new Runnable() {

				@Override
				public void run() {
					try{
						/* Drinks query */
						gui.SetMessage("Searching Drinks...");

						IDrinksOrderDetails DrinksOrderDetails = orderDetails.GetDrinksDetails();
						ArrayList<Pair<String, Integer>> DrinksResult = DataBaseManaging.getDrinks(DrinksOrderDetails);

						if (DrinksResult != null){
							gui.SetMessage("Search came up with "+DrinksResult.size()+" Drinks.");

							IQueryResult DrinksQueryResult = new QueryResult(DishTypes.DRINKS, DrinksResult);
							gui.SetQueryResult(DrinksQueryResult);

						}

						else
						{
							gui.SetError(new Error(false,"Data Base Error: Failed to query the DataBase for drinks."));
						}
					}
					catch(SQLException e){
						gui.SetError(new Error(false,"Data Base Error: Failed to query the DataBase for drinks."));
						gui.SetMessage("Query for drinks Error: "+e.getMessage());
					}
				}
			});
		}

		if(orderDetails.dessert())
			ThreadManaging.Execute(new Runnable() {

				@Override
				public void run() {

					/* Dessert query */
					try{
						gui.SetMessage("Searching Desserts...");

						int[] dessertOrderDetails = generateDessertOrder();

						ArrayList<Pair<String, Integer>> DessertResult = DataBaseManaging.getDessert(dessertOrderDetails);


						if (DessertResult != null){

							for (Pair<String, Integer> pair : DessertResult) {
								pair.setSecond(0);
							}

							gui.SetMessage("Search came up with "+DessertResult.size()+" Desserts.");

							IQueryResult DessertQueryResult = new QueryResult(DishTypes.DESSERT, DessertResult);
							gui.SetQueryResult(DessertQueryResult);
						}
						else
							gui.SetError(new Error(false,"Data Base Error: Failed to query the DataBase for desserts."));
					}
					catch (SQLException e){
						gui.SetError(new Error(false,"Data Base Error: Failed to query the DataBase for desserts."));
						gui.SetMessage("Query for desserts Error: "+e.getMessage());
					}

				}

			});

	}

	private int[] generateDessertOrder() {
		Random random = new Random();

		int movie_id1 = random.nextInt(500000)+1;
		int movie_id2 = random.nextInt(500000)+1;
		int movie_id3 = random.nextInt(500000)+1;

		return new int[]{movie_id1,movie_id2,movie_id3};

	}

	@Override
	public void ButtonPushed(Operation operation) {

		switch (operation){

		case IMPORT: ImportButtonPushed();
		break;	
		case ORDER: OrderButtonPushed(gui.GetOrderDetails());
		break;
		case FIND_MOVIE: FindMovieButtonPushed();
		break;
		case FIND_ACTOR: FindActorButtonPushed();
		break;
		case FIND_TV_SHOW: FindTvShowButtonPushed();
		break;
		case EDIT_MOVIE: EditMovieButtonPushed();
		break;
		case EDIT_ACTOR: EditActorButtonPushed();
		break;
		case EDIT_TV_SHOW: EditTvShowButtonPushed();
		break;
		case DELETE_MOVIE: DeleteMovieButtonPushed();
		break;
		case DELETE_ACTOR: DeleteActorButtonPushed();
		break;
		case DELETE_TV_SHOW: DeleteTvShowButtonPushed();
		break;
		case ADD_MOVIE: AddMovieButtonPushed();
		break;
		case ADD_ACTOR: AddActorButtonPushed();
		break;
		case ADD_TV_SHOW: AddTvShowButtonPushed();
		break;
		case FIND_ACTOR_FOR_ADD_CONNECTION: findActorForAddConnectionButtonPushed();
		break;
		case FIND_MOVIE_FOR_ADD_CONNECTION: findMovieForAddConnectionButtonPushed();
		break;
		case MAKE_ACTOR_MOVIE_CONNECTION: MakeActorMovieConnectionButtonPushed();
		break;
		case FIND_ACTOR_FOR_MAIN_COURSE: findActorForMainCourseButtonPushed();
		break;
		default: gui.SetError(new Error(false,"Unknown Operation"));

		}

	}

	private void findActorForMainCourseButtonPushed() {

		ThreadManaging.Execute(new Runnable() {

			@Override
			public void run() {

				actorDB actor = gui.getActorParametersForMainCourse(); 

				try {
					gui.SetMessage("searching actors...");
					Map<Integer, String> actorsFound = DataBaseManaging.findActor(actor.getFirstName(),actor.getLastName());

					if(actorsFound != null){
						gui.SetMessage("search came up with "+actorsFound.size()+ " actors");
						gui.setActorsFoundForMainCourse(actorsFound);
					}
					else{
						gui.SetError(new Error(false,"Data Base Error: Failed to find actors for main course."));
						gui.setActorsFoundForMainCourse(null);
					}

				} catch (Exception e) {
					gui.SetError(new Error(false,"Data Base Error: Failed to find actors for main course."));
					gui.SetMessage("Findind actord Error: " +e.getMessage());
					gui.setActorsFoundForMainCourse(null);
				}


			}
		});


	}

	private void MakeActorMovieConnectionButtonPushed() {
		ThreadManaging.Execute(new Runnable() {

			@Override
			public void run() {
				DataBaseEditor.MakeActorMovieConnection();

			}
		});

	}

	private void findMovieForAddConnectionButtonPushed() {
		ThreadManaging.Execute(new Runnable() {

			@Override
			public void run() {
				DataBaseEditor.findMovieForAddConnection();

			}
		});

	}

	private void findActorForAddConnectionButtonPushed() {
		ThreadManaging.Execute(new Runnable() {

			@Override
			public void run() {
				DataBaseEditor.findActorForAddConnection();

			}
		});


	}

	private void AddTvShowButtonPushed() {
		DataBaseEditor.addTvShow();

	}

	private void AddActorButtonPushed() {
		DataBaseEditor.addActor();

	}

	private void AddMovieButtonPushed() {
		ThreadManaging.Execute(new Runnable() {

			@Override
			public void run() {
				DataBaseEditor.addMovie();

			}
		});


	}

	private void DeleteTvShowButtonPushed() {
		ThreadManaging.Execute(new Runnable() {

			@Override
			public void run() {
				DataBaseEditor.deleteTvShow();		
			}
		});


	}

	private void DeleteActorButtonPushed() {
		DataBaseEditor.deleteActor();

	}

	private void DeleteMovieButtonPushed() {
		ThreadManaging.Execute(new Runnable() {

			@Override
			public void run() {
				DataBaseEditor.deleteMovie();

			}
		});


	}

	private void EditTvShowButtonPushed() {
		ThreadManaging.Execute(new Runnable() {

			@Override
			public void run() {

				DataBaseEditor.editTvShow();

			}
		});

	}

	private void EditActorButtonPushed() {
		ThreadManaging.Execute(new Runnable() {

			@Override
			public void run() {

				DataBaseEditor.editActor();

			}
		});

	}

	private void EditMovieButtonPushed() {
		ThreadManaging.Execute(new Runnable() {

			@Override
			public void run() {

				DataBaseEditor.editMovie();

			}
		});

	}

	private void FindTvShowButtonPushed() {

		ThreadManaging.Execute(new Runnable() {

			@Override
			public void run() {

				DataBaseEditor.findTvShow();

			}
		});

	}

	private void FindActorButtonPushed() {

		ThreadManaging.Execute(new Runnable() {

			@Override
			public void run() {

				DataBaseEditor.findActors();

			}
		});
	}

	private void FindMovieButtonPushed() {
		ThreadManaging.Execute(new Runnable() {

			@Override
			public void run() {

				DataBaseEditor.findMovies();

			}
		});

	}


	@Override
	public void Stop() {

		gui.SetMessage("Shutting Down DataBase Connection");

		try {
			DataBaseManaging.ShutDown();
			gui.SetMessage("DataBase Connection: ShutDown succeed");

		} catch (SQLException e) {
			gui.SetError(new Error(false,"Data Base Error: Failed to shut down data base conneciton."));
			gui.SetMessage("Shut down Data Base Error: "+e.getMessage());
		}

		gui.SetMessage("Shutting Down ThreadManager");
		ThreadManaging.ShutDown();
		gui.SetMessage("ThreadManager: ShutDown succeed");

		gui.close();
	}

	public static IGui getGui(){
		return gui;
	}
}


